Skip to content

test(swift-sdk): mid flight added wallet integration test#4064

Open
ZocoLini wants to merge 1 commit into
v4.1-devfrom
test/mid-flight-wallet
Open

test(swift-sdk): mid flight added wallet integration test#4064
ZocoLini wants to merge 1 commit into
v4.1-devfrom
test/mid-flight-wallet

Conversation

@ZocoLini

@ZocoLini ZocoLini commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

What was done?

How Has This Been Tested?

Breaking Changes

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Tests
    • Added integration coverage validating that wallets imported during an in-progress synchronization correctly backfill their complete transaction history.
    • Verified balances are accurately restored after importing a wallet with multiple funded addresses.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Swift integration test that funds multiple wallet addresses, imports the wallet during SPV synchronization with birthHeight: 0, and verifies that full historical balance is recovered. It also adds FFI address-derivation and error-message cleanup helpers.

Changes

SPV mid-sync backfill

Layer / File(s) Summary
Address derivation and FFI error handling
packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift
Derives external addresses through the Rust/FFI layer and safely converts and frees unmanaged address and error-message data.
Mid-sync wallet history backfill test
packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift
Funds multiple addresses, starts SPV synchronization, imports the wallet with birthHeight: 0, waits for synchronization, and asserts the expected aggregate balance.

Estimated code review effort: 3 (Moderate) | ~15 minutes

Possibly related PRs

  • dashpay/platform#3712: Adds integration-test harness support used by this Swift MID-sync backfill test.
  • dashpay/platform#4062: Addresses wallet registration balance handling for the same MID-sync import scenario.

Suggested reviewers: QuantumExplorer, shumkov, thepastaclaw, llbartekll, lklimek

Sequence Diagram(s)

sequenceDiagram
  participant Test as IntegrationTest
  participant WalletManager
  participant SPVClient
  participant ImportedWallet

  Test->>WalletManager: Derive and fund multiple external addresses
  Test->>SPVClient: Start synchronization
  Test->>SPVClient: Import wallet with birthHeight 0
  SPVClient->>ImportedWallet: Backfill wallet history
  ImportedWallet-->>Test: Return synchronized balance
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is relevant to the changeset and captures the main addition of a mid-sync wallet integration test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/mid-flight-wallet

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 9, 2026
@ZocoLini ZocoLini force-pushed the test/mid-flight-wallet branch from dc58d76 to 6479b65 Compare July 9, 2026 14:24
@ZocoLini ZocoLini force-pushed the test/mid-flight-wallet branch from 6479b65 to e40cd58 Compare July 10, 2026 18:52
@ZocoLini ZocoLini changed the title Test/mid flight wallet test(swift-sdk): mid flight added wallet integration test Jul 10, 2026
@ZocoLini ZocoLini marked this pull request as ready for review July 10, 2026 18:52
@thepastaclaw

thepastaclaw commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

✅ Review complete (commit e40cd58)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift`:
- Line 13: Replace the floating-point amountEachDuffs calculation with an
integer Duffs constant or integer-based representation, and update
amountEachDash usage as needed so values such as 0.0001 map exactly to 10000
Duffs without floating-point multiplication or truncation.
- Around line 129-136: Add a post-loop assertion after the address collection
loop in the relevant address-derivation helper, verifying that result.count
equals outCount and reporting a clear failure message if not, before any
subsequent funding-loop indexing occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7cebbd2-68e1-46bf-817f-02d30eaf29db

📥 Commits

Reviewing files that changed from the base of the PR and between f7d7c8d and e40cd58.

📒 Files selected for processing (1)
  • packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift


/// Funding amount per address, in DASH.
private let amountEachDash: Double = 0.001
private var amountEachDuffs: UInt64 { UInt64(amountEachDash * 1e8) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Floating-point conversion for Duffs is fragile.

UInt64(amountEachDash * 1e8) works for 0.001 (truncates 100000.000…002 to 100000), but breaks for other values — e.g. 0.0001 * 1e8 = 9999.999… truncates to 9999 instead of 10000. Define the amount in integer Duffs directly to avoid precision-dependent truncation.

🛡️ Proposed fix
-    private let amountEachDash: Double = 0.001
-    private var amountEachDuffs: UInt64 { UInt64(amountEachDash * 1e8) }
+    private let amountEachDuffs: UInt64 = 100_000 // 0.001 DASH
+    private var amountEachDash: Double { Double(amountEachDuffs) / 1e8 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private var amountEachDuffs: UInt64 { UInt64(amountEachDash * 1e8) }
private let amountEachDuffs: UInt64 = 100_000 // 0.001 DASH
private var amountEachDash: Double { Double(amountEachDuffs) / 1e8 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift`
at line 13, Replace the floating-point amountEachDuffs calculation with an
integer Duffs constant or integer-based representation, and update
amountEachDash usage as needed so values such as 0.0001 map exactly to 10000
Duffs without floating-point multiplication or truncation.

Comment on lines +129 to +136
var result: [String] = []
result.reserveCapacity(outCount)
for i in 0..<outCount {
if let cstr = arr[i] {
result.append(String(cString: cstr))
}
}
return result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate derived address count matches requested count.

If the FFI returns fewer addresses than count (e.g. null entries skipped by the if let), result will have fewer elements and the subsequent addresses[i] access in the funding loop will crash with an unhelpful index-out-of-bounds error. Add a post-loop assertion to fail fast with a clear message.

🛡️ Proposed fix
     for i in 0..<outCount {
         if let cstr = arr[i] {
             result.append(String(cString: cstr))
         }
     }
+    guard result.count == count else {
+        throw DerivationError(
+            description: "deriveExternalAddresses: expected \(count) addresses, got \(result.count)"
+        )
+    }
     return result
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var result: [String] = []
result.reserveCapacity(outCount)
for i in 0..<outCount {
if let cstr = arr[i] {
result.append(String(cString: cstr))
}
}
return result
var result: [String] = []
result.reserveCapacity(outCount)
for i in 0..<outCount {
if let cstr = arr[i] {
result.append(String(cString: cstr))
}
}
guard result.count == count else {
throw DerivationError(
description: "deriveExternalAddresses: expected \(count) addresses, got \(result.count)"
)
}
return result
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift`
around lines 129 - 136, Add a post-loop assertion after the address collection
loop in the relevant address-derivation helper, verifying that result.count
equals outCount and reporting a clear failure message if not, before any
subsequent funding-loop indexing occurs.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Source: Sol orchestrator openai/gpt-5.6-sol (high, orchestration-only); reviewers: Sol gpt-5.6-sol and Sonnet claude-sonnet-5 for general, security-auditor, and ffi-engineer; verifier: Sonnet claude-sonnet-5; Opus: not sampled (bucket 3).

This PR adds a single new Swift integration test (SpvManyTxMidSyncBackfillIntegrationTests.swift, +148 lines, no other files touched — confirmed against parent commit b42d213 at head e40cd58). The test's core FFI usage for bulk address derivation is correct, but it leaks a Rust-owned FFIWallet handle behind an incorrect ownership comment, bypasses the suite's InstantSend-aware funding helper, doesn't actually guarantee the mid-sync precondition it's named for, and only checks aggregate balance rather than full transaction history despite the test name promising 'finds all tx'.

🟡 4 suggestion(s) | 💬 2 nitpick(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift`:
- [SUGGESTION] packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift:101-110: Leaked FFIWallet from wallet_manager_get_wallet; ownership comment is factually wrong
  I checked the pinned key-wallet-ffi source directly (rev 647fa9820f3614090e4e5f5f2b709961d68e538b, cached at ~/.cargo/git/checkouts/rust-dashcore-*/647fa98/key-wallet-ffi/src/wallet_manager.rs:371-388). `wallet_manager_get_wallet` clones the wallet from the manager (`.cloned()`) and returns a brand-new heap allocation every call: `Box::into_raw(Box::new(FFIWallet::new(wallet)))`. Its doc comment is explicit: 'The returned wallet must be freed with wallet_free_const()', and `wallet_free_const` (wallet.rs:278-279) names this exact function by name as a caller of it. The manager does NOT retain ownership of this specific pointer.

  The comment on line 101, 'Non-owning: the manager retains ownership of the wallet handle,' is incorrect, and there is no `defer { wallet_free_const(wallet) }` anywhere in `deriveExternalAddresses`. Every test run leaks one boxed `FFIWallet`. The same incorrect assumption already exists in production `WalletManager.swift` (confirmed: `wallet_free_const` is never called there despite 3 call sites of `wallet_manager_get_wallet`), so this is a pre-existing pattern rather than something this PR invented — but this PR adds a new call site with an incorrect rationale restated verbatim, and it's easy to fix here.
- [SUGGESTION] packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift:36-43: "~20% synced" precondition is not actually guaranteed — it depends on prior tests' chain growth, not this test's own state
  I traced the harness: `IntegrationTestEnv.bootstrap()` (IntegrationTestEnv.swift:113-165) runs exactly once per test process, syncs the SPV data dir to the chain tip at that moment via `createSpvCache()`, then snapshots it with `snapshotSpvCache()`. Every subsequent test's `tearDown` calls `resetState()` → `restoreSpvCacheFromSnapshot()` (lines 78-82, 223-241), which restores the working SPV dir to that *same* pre-synced snapshot before the next test runs. Meanwhile the underlying regtest node keeps mining blocks across the whole suite — it is never reset.

  So when this test calls `waitUntilUpToDate(height: tipHeight / 5)`, whether that wait does any real syncing work is entirely a function of how far the live chain has grown past the bootstrap-time snapshot height (i.e., how many blocks earlier tests in the same run happened to mine), not anything this test controls or asserts. `waitUntilUpToDate` (TestWallet.swift:69-89) returns as soon as cached headers/filters already meet the target — if the snapshot's cached height already exceeds `tipHeight/5` (plausible after any funding-heavy tests ran first), this reduces to importing into an already-synced client, silently testing the same late-import scenario `SpvLateWalletBackfillIntegrationTests` already covers instead of a genuine mid-flight race. Nothing in the test verifies headers/filters are actually below the current chain tip before `createWallet` runs.
- [SUGGESTION] packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift:55-69: Test name promises 'finds all tx' but only aggregate balance is asserted, not transaction history
  The test method is `testManyTxImportedMidSyncBackfillsAllHistory` and the PR title says 'finds all tx', but the only assertion (lines 65-69) checks `imported.balance().total == expectedTotal`. Balance/UTXO tracking and transaction-history persistence are separate write paths — `PlatformWalletPersistenceHandler` independently writes rows into `PersistentTransaction`. A regression that restores the correct aggregate balance while dropping one or more of the 10 individual transaction records from history would pass this test undetected, since the 10 funding txids are never captured or checked against the imported wallet's history.
- [SUGGESTION] packages/swift-sdk/SwiftTests/SwiftDashSDKIntegrationTests/Core/SpvManyTxMidSyncBackfillIntegrationTests.swift:26-32: Funding loop bypasses `env.fund()`'s InstantSend/masternode-broadcast steps
  I confirmed `env.fund(address:dash:)` (IntegrationTestEnv.swift:391-398) does `sendToAddress` → `broadcastToMasternodes(txid:)` → `waitForInstantSendLock(txid:)` → `mine(1)`, and every other test in this suite (`SpvLateWalletBackfillIntegrationTests`, `CoreSendIntegrationTests`, `SpvRestartIntegrationTests`, etc.) uses this helper. This new test instead calls `env.coreRPC.sendToAddress` directly and immediately `env.mine(1)`, skipping the masternode broadcast and InstantSend-lock wait. Since this test funds 10 separate addresses across 10 blocks in quick succession, it's the test most likely to expose the absence of that synchronization the rest of the suite already guards against, making it a plausible source of CI flakiness.

Comment on lines +101 to +110
// Non-owning: the manager retains ownership of the wallet handle.
guard let wallet = walletId.withUnsafeBytes({ raw in
wallet_manager_get_wallet(
manager.ffiHandle,
raw.bindMemory(to: UInt8.self).baseAddress,
&error
)
}) else {
throw DerivationError(description: "wallet_manager_get_wallet failed: \(Self.take(&error))")
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Leaked FFIWallet from wallet_manager_get_wallet; ownership comment is factually wrong

I checked the pinned key-wallet-ffi source directly (rev 647fa9820f3614090e4e5f5f2b709961d68e538b, cached at ~/.cargo/git/checkouts/rust-dashcore-*/647fa98/key-wallet-ffi/src/wallet_manager.rs:371-388). wallet_manager_get_wallet clones the wallet from the manager (.cloned()) and returns a brand-new heap allocation every call: Box::into_raw(Box::new(FFIWallet::new(wallet))). Its doc comment is explicit: 'The returned wallet must be freed with wallet_free_const()', and wallet_free_const (wallet.rs:278-279) names this exact function by name as a caller of it. The manager does NOT retain ownership of this specific pointer.

The comment on line 101, 'Non-owning: the manager retains ownership of the wallet handle,' is incorrect, and there is no defer { wallet_free_const(wallet) } anywhere in deriveExternalAddresses. Every test run leaks one boxed FFIWallet. The same incorrect assumption already exists in production WalletManager.swift (confirmed: wallet_free_const is never called there despite 3 call sites of wallet_manager_get_wallet), so this is a pre-existing pattern rather than something this PR invented — but this PR adds a new call site with an incorrect rationale restated verbatim, and it's easy to fix here.

Suggested change
// Non-owning: the manager retains ownership of the wallet handle.
guard let wallet = walletId.withUnsafeBytes({ raw in
wallet_manager_get_wallet(
manager.ffiHandle,
raw.bindMemory(to: UInt8.self).baseAddress,
&error
)
}) else {
throw DerivationError(description: "wallet_manager_get_wallet failed: \(Self.take(&error))")
}
// Owning: the manager clones the wallet on each call; the caller
// must free it with wallet_free_const().
guard let wallet = walletId.withUnsafeBytes({ raw in
wallet_manager_get_wallet(
manager.ffiHandle,
raw.bindMemory(to: UInt8.self).baseAddress,
&error
)
}) else {
throw DerivationError(description: "wallet_manager_get_wallet failed: \(Self.take(&error))")
}
defer { wallet_free_const(wallet) }

source: ['codex-ffi-engineer', 'sonnet5-ffi-engineer']

Comment on lines +36 to +43
// Start the SPV client EMPTY, then wait until it is ~20% synced so the
// wallet is imported genuinely mid-sync.
try await env.walletManager.startSpv(config: env.spvConfig)
let tipHeight = try await env.coreRPC.getBlockCount()
try await env.walletManager.waitUntilUpToDate(
height: tipHeight / 5,
timeout: 180
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: "~20% synced" precondition is not actually guaranteed — it depends on prior tests' chain growth, not this test's own state

I traced the harness: IntegrationTestEnv.bootstrap() (IntegrationTestEnv.swift:113-165) runs exactly once per test process, syncs the SPV data dir to the chain tip at that moment via createSpvCache(), then snapshots it with snapshotSpvCache(). Every subsequent test's tearDown calls resetState()restoreSpvCacheFromSnapshot() (lines 78-82, 223-241), which restores the working SPV dir to that same pre-synced snapshot before the next test runs. Meanwhile the underlying regtest node keeps mining blocks across the whole suite — it is never reset.

So when this test calls waitUntilUpToDate(height: tipHeight / 5), whether that wait does any real syncing work is entirely a function of how far the live chain has grown past the bootstrap-time snapshot height (i.e., how many blocks earlier tests in the same run happened to mine), not anything this test controls or asserts. waitUntilUpToDate (TestWallet.swift:69-89) returns as soon as cached headers/filters already meet the target — if the snapshot's cached height already exceeds tipHeight/5 (plausible after any funding-heavy tests ran first), this reduces to importing into an already-synced client, silently testing the same late-import scenario SpvLateWalletBackfillIntegrationTests already covers instead of a genuine mid-flight race. Nothing in the test verifies headers/filters are actually below the current chain tip before createWallet runs.

source: ['codex-general']

Comment on lines +55 to +69
// Wait until the wallet is fully synced, then check that the balance is
try await env.walletManager.waitUntilUpToDate(
height: try await env.coreRPC.getBlockCount(),
timeout: 180
)
_ = try? await Wait.until("balance reaches expected", timeout: 120, pollInterval: 0.5) {
try imported.balance().total == expectedTotal
}
let atFullSync = try imported.balance().total

XCTAssertEqual(
atFullSync,
expectedTotal,
"atFullSync=\(atFullSync) expected=\(expectedTotal)"
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Test name promises 'finds all tx' but only aggregate balance is asserted, not transaction history

The test method is testManyTxImportedMidSyncBackfillsAllHistory and the PR title says 'finds all tx', but the only assertion (lines 65-69) checks imported.balance().total == expectedTotal. Balance/UTXO tracking and transaction-history persistence are separate write paths — PlatformWalletPersistenceHandler independently writes rows into PersistentTransaction. A regression that restores the correct aggregate balance while dropping one or more of the 10 individual transaction records from history would pass this test undetected, since the 10 funding txids are never captured or checked against the imported wallet's history.

source: ['codex-general']

Comment on lines +26 to +32
// One funding tx per block.
for i in 0..<numTxs {
_ = try await env.coreRPC.sendToAddress(
amount: amountEachDash, address: addresses[i]
)
_ = try await env.mine(1)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Funding loop bypasses env.fund()'s InstantSend/masternode-broadcast steps

I confirmed env.fund(address:dash:) (IntegrationTestEnv.swift:391-398) does sendToAddressbroadcastToMasternodes(txid:)waitForInstantSendLock(txid:)mine(1), and every other test in this suite (SpvLateWalletBackfillIntegrationTests, CoreSendIntegrationTests, SpvRestartIntegrationTests, etc.) uses this helper. This new test instead calls env.coreRPC.sendToAddress directly and immediately env.mine(1), skipping the masternode broadcast and InstantSend-lock wait. Since this test funds 10 separate addresses across 10 blocks in quick succession, it's the test most likely to expose the absence of that synchronization the rest of the suite already guards against, making it a plausible source of CI flakiness.

source: ['sonnet5-general']

birthHeight: 0
)

// Wait until the wallet is fully synced, then check that the balance is

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Nitpick: Comment is truncated mid-sentence

"Wait until the wallet is fully synced, then check that the balance is" cuts off before completing the thought. Confirmed verbatim in the file.

Suggested change
// Wait until the wallet is fully synced, then check that the balance is
// Wait until the wallet is fully synced, then check that the balance
// reaches the expected total.

source: ['sonnet5-general']

Comment on lines +22 to +29
let addresses = try Self.deriveExternalAddresses(
manager: km, walletId: walletId, count: numTxs
)

// One funding tx per block.
for i in 0..<numTxs {
_ = try await env.coreRPC.sendToAddress(
amount: amountEachDash, address: addresses[i]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Nitpick: No guard that deriveExternalAddresses returned exactly numTxs addresses before indexing

deriveExternalAddresses skips any null C-string entries when building result (line 132: if let cstr = arr[i]), so it could in principle return fewer than count addresses. The funding loop then indexes addresses[i] for i in 0..<numTxs unconditionally, which would crash with an out-of-range index rather than a clear assertion failure if that ever happened. Low likelihood given the FFI contract, but a cheap XCTAssertEqual(addresses.count, numTxs) before the loop would fail more legibly.

source: ['sonnet5-general']

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants